home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / MIDI Manager Class Library / Lefty.ƒ / Lefty.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-03-07  |  10.7 KB  |  354 lines  |  [TEXT/KAHL]

  1. /*
  2.  *--- Lefty.c -----------------------------------------------------------
  3.  * Copyright © Paul D. Ferguson, 1990.  All rights reserved.
  4.  *
  5.  *    This source code is a complete THINK Class Library application for
  6.  *    use with Apple's MIDI Manager.
  7.  *
  8.  *    This program is freeware, although I retain all rights to it.  You
  9.  *    are free to distribute it provided you don't sell it.
  10.  *
  11.  *    If you have any comments about this program, I can be reached on
  12.  *    CompuServe at 70441,3055.
  13.  *
  14.  *
  15.  * Description:
  16.  *    This program has no user interface to speak of.  It's main purpose is
  17.  *    to illustrate the use of the CMIDI objects.  These objects are
  18.  *    described in CMIDI.c.
  19.  *
  20.  *    Like a left-handed guitar, Lefty makes your MIDI keyboard into a
  21.  *    “left-handed keyboard”.
  22.  *
  23.  *    It reverses the note numbers of Note On, Note Off, and Aftertouch
  24.  *    MIDI messages, i.e. the low notes are towards the right end of the
  25.  *    keyboard, while the high notes are towards the left.  All other
  26.  *    MIDI messages are simply passed straight through without change.
  27.  *
  28.  *    The note values are also calculated so that the black keys are
  29.  *    reversed correctly to complete the illusion.  Thus when filtering
  30.  *    through Lefty, middle C is found on the E key.  The graphic
  31.  *    in the “About Lefty” box illustrates this.
  32.  *
  33.  * Usage:
  34.  * ------
  35.  *    You must have Apple's MIDI Manager (version 1.2 or later) software
  36.  *    to use this program.  In the simplest configuration, just route the
  37.  *    MIDI Manager output port through Lefty, and back into the input port.
  38.  *
  39.  *    You can also play back songs from a sequencer through Lefty for some
  40.  *    pretty unusual effects.
  41.  *
  42.  *    To build this application, make a copy of Starter.π project file 
  43.  *    and Starter.π.rsrc resource file.  In the project, you can delete all
  44.  *    the "Starter" files (e.g. StartApp.c, StarterDoc.c, etc.), and add
  45.  *    this file (Lefty.c), CMIDI.c, and the MIDIGlue library.
  46.  *
  47.  *
  48.  * Implementation Notes:
  49.  * ---------------------
  50.  *    This source code is for use THINK C 5.0 and the THINK Class Library.
  51.  *    Since this program is so simple, I have reduced the application to
  52.  *    this single source file.  Although Lefty doesn’t use most of TCL's
  53.  *    features, you still need to include them in the project.  As a
  54.  *    result, the application is about 110K+ in size which is “kind of large”.
  55.  *    Oh well, memory is cheap... 
  56.  *
  57.  *    If you really wish to use this program, and memory is an issue, you
  58.  *    should be able to rip out all the TCL files.  All you need is to
  59.  *    handle the menu events.  You should be able to get the application
  60.  *    down to under 20K, and requiring under 100K partition size.
  61.  *
  62.  *    The other files in the project include CMIDI.c and MIDIGlue library.
  63.  *    MIDIGlue is a library created from the MPW object file (using
  64.  *    oconv) which comes with the MIDI Manager developer’s kit.  The
  65.  *    header file MIDI.h referenced in CMIDI.c is likewise from the kit,
  66.  *    with several minor modifications in order to be compatible with
  67.  *    THINK C.  Refer the the CMIDI Programmer’s Reference for more info.
  68.  *
  69.  *---------------------------------------------------------------------
  70.  */
  71.  
  72. #include <CApplication.h>
  73. #include <Commands.h>
  74. #include <CBartender.h>
  75. #include <CDesktop.h>
  76. #include <CError.h>
  77.  
  78. #include <CMIDIClient.h>
  79. #include <CMIDIInputPort.h>
  80. #include <CMIDIOutputPort.h>
  81.  
  82. #define PORTNAMES            1024        // Resource ID for strings
  83. #define ABOUTBOX            1024        // An ALRT
  84.  
  85. #define FLIPNOTE            62            // Point at which to flip note numbers
  86.  
  87. #define INBUFSIZE            30000        // Nice big buffer
  88.  
  89. #define menuLEFTY            128            // MENU ID for DoCommand()
  90. #define cmdEnable            12801        // Only two commands needed
  91. #define cmdDisable            12802
  92.  
  93. #define    TIMEPORT            128
  94. #define INPORT                129
  95. #define OUTPORT                130
  96.  
  97. struct CLeftyApp : CApplication            // This is Lefty...
  98. {
  99.     CMIDIInputPort *    itsMIDIIn;
  100.     CMIDIOutputPort *    itsMIDIOut;
  101.     CMIDITimePort *        itsMIDITime;
  102.     
  103.     void                ILeftyApp(void);
  104.     void                UpdateMenus(void);    // Override a couple methods
  105.     void                DoCommand(long theCommand);
  106.     void                Exit(void);
  107. };
  108.  
  109. /*
  110.  * Referenced externals
  111.  */
  112. extern    OSType               gSignature;
  113. extern    CMIDIClient        * gMIDIClient;
  114. extern    CApplication    * gApplication;
  115. extern    CBartender        * gBartender;
  116. extern    CError            * gError;
  117.  
  118. /*
  119.  * These variables are used locally.  Because we're running
  120.  * in interrupt context for the MIDI handler, we don't want
  121.  * to go through the overhead of putting these into objects,
  122.  * only to have to extract them for the handler.
  123.  */
  124. unsigned char    newNote[128];        // Note translation table
  125. int                gMIDIOutRefNum;        // For direct MIDI Manager Writes…
  126.  
  127. /*
  128.  *--- main ------------------------------------------------------
  129.  */
  130. void main()
  131. {
  132.     gApplication = new(CLeftyApp);
  133.     ((CLeftyApp *)gApplication)->ILeftyApp();
  134.     gApplication->Run();
  135.     gApplication->Exit();
  136. }
  137.  
  138. /*
  139.  *--- LeftyReadHook -----------------------------------------------
  140.  * This is our application's MIDI Manager read function.  It 
  141.  * normally operates in interrupt context.
  142.  *
  143.  * This function looks to see whether the packet is a Note On,
  144.  * Note Off, or Poly After Pressure.  If so, it translates the 
  145.  * key number.  All other messages are passed straight through.
  146.  *
  147.  * NOTE: This read hook does not properly handle MIDI messages
  148.  *       which contain two or more Note On/Off messages (e.g.
  149.  *       80 66 7F 70 7F 75 7F ...).  If you need this, then you'll
  150.  *       just have to add it yourself.
  151.  *------------------------------------------------------------------
  152.  */
  153. pascal short LeftyReadHook(MIDIPacket * ThePacketPtr, long TheRefCon)
  154. {
  155.     long            SysA5 = SetA5(TheRefCon);
  156.     register unsigned char    theFlags;
  157.     register unsigned int    it;
  158.  
  159.     theFlags = ThePacketPtr->flags;
  160.     if (theFlags & midiMgrType)            // Ignore error packets.    
  161.     {
  162.         SetA5(SysA5);
  163.         return(midiMorePacket);            // Throw away        
  164.     }
  165. /*
  166.  * Do we want to alter this packet?
  167.  */    
  168.     if (   (ThePacketPtr->len == 9)            // six byte header & three MIDI bytes 
  169.         && (ThePacketPtr->data[0] >= 0x80)    // is Note On, Note Off, or Key Pressure    
  170.         && (ThePacketPtr->data[0] <= 0xAF)
  171.         && ((theFlags & midiContMask) == midiNoCont) )    // Is a simple packet    
  172.     {
  173.         it = ThePacketPtr->data[1];        // translate the note byte    
  174.         ThePacketPtr->data[1] = newNote[it];    // using a lookup table        
  175.     }
  176. /*
  177.  * In many cases, it’s just as easy to call the MIDI Manager directly,
  178.  * as in this case of echoing each packet back out, assuming you 
  179.  * already know that the MIDI Manager is present and functioning.
  180.  *
  181.  * Presumably, because this code wouldn’t get executed at all unless
  182.  * that were the case, we can make this assumption.
  183.  */
  184.      MIDIWritePacket(gMIDIOutRefNum, ThePacketPtr);
  185.     SetA5(SysA5);
  186.     return (midiMorePacket);            // Get next packet        
  187. }
  188.  
  189. /*
  190.  *--- NormalReadHook -----------------------------------------------
  191.  * This is the “Disabled” readHook.  It simply passes every packet
  192.  * straight through.
  193.  *------------------------------------------------------------------
  194.  */
  195. pascal short NormalReadHook(MIDIPacket * ThePacketPtr, long TheRefCon)
  196. {
  197.     long        SysA5 = SetA5(TheRefCon);
  198.  
  199.     if (! (ThePacketPtr->flags & midiMgrType) )    // Ignore error packets.
  200.     {
  201.          MIDIWritePacket(gMIDIOutRefNum, ThePacketPtr);
  202.     }
  203.     SetA5(SysA5);
  204.     return (midiMorePacket);            // Get next packet
  205. }
  206.  
  207.  
  208. pascal void myConnectProc(short refnum, long refcon, short portType,
  209.      OSType clientID, OSType portID,
  210.      Boolean connect, short direction)
  211. {
  212.     portType &= midiPortTypeMask;
  213.     if (portType == midiPortTypeTime)
  214.     {
  215.         if (direction == midiInternalSync)
  216.         {
  217.             // There's stuff to be done here...
  218.         }
  219.         else
  220.         {
  221.             // but I ain't doin' it...
  222.         }
  223.     }
  224. }
  225.  
  226. /*
  227.  *--- ILeftyApp -------------------------------------------------
  228.  * Initialize the application.  Most of the work here is in
  229.  * creating and initializing the CMIDI objects.
  230.  *---------------------------------------------------------------
  231.  */
  232. void CLeftyApp::ILeftyApp(void)
  233. {
  234.     register int     i;
  235.     Str255            theString;
  236.     OSErr            theResult;
  237.  
  238.     gSignature = 'Left';
  239.     
  240.     CApplication::IApplication(4, 20480L, 2048L, 2048L);
  241.     DoCommand(cmdAbout);
  242.  
  243.     gBartender->SetDimOption(menuLEFTY, dimNONE);
  244.     gBartender->SetUnchecking(menuLEFTY, TRUE);
  245.  
  246. /*
  247.  * Set up note mapping table.  By using a table, this program
  248.  * can be generalized for any kind of mappings.  Simply set the
  249.  * netNote[] array to the appropriate values.
  250.  */
  251.     for (i = 0; i < 128; ++i)
  252.         newNote[i] = 0;                // just being sure
  253.  
  254.     for (i = 0; i < (2*FLIPNOTE) ; ++i)
  255.         newNote[i] = (2*FLIPNOTE) - i;
  256. /*
  257.  * Sign into MIDI Manager.  Open input and output ports.
  258.  * Also open a time port, although we don't use it.
  259.  */
  260.  
  261.     gMIDIClient = new(CMIDIClient);
  262.     theResult = gMIDIClient->IMIDIClient(128);
  263.     if (! gError->CheckOSError(theResult))        // Can't go on...
  264.         DoCommand(cmdQuit);
  265.  
  266.                                                 // Time base
  267.     GetIndString(theString, PORTNAMES, 3);
  268.     itsMIDITime = new(CMIDITimePort);            // We don't actually use this time port    
  269.     itsMIDITime->IMIDITimePort(theString, 'ATim', TRUE, midiFormatMSec);        
  270.     itsMIDITime->LoadPatches('Port', TIMEPORT);
  271.     itsMIDITime->SetConnection(&myConnectProc);
  272.  
  273.                                                 // Output port
  274.     GetIndString(theString, PORTNAMES, 2);
  275.     itsMIDIOut = new(CMIDIOutputPort);
  276.     itsMIDIOut->IMIDIOutputPort(theString, 'Out ',
  277.              TRUE, itsMIDITime, midiGetCurrent);
  278.     itsMIDIOut->LoadPatches('Port', OUTPORT);    // Did we have any patches?    
  279.     gMIDIOutRefNum = itsMIDIOut->GetRefNum();    // Save for reference in our read hook
  280.  
  281.                                                 // Input port
  282.     GetIndString(theString, PORTNAMES, 1);
  283.     itsMIDIIn = new(CMIDIInputPort);
  284.     itsMIDIIn->IMIDIInputPort(theString, 'In  ', 
  285.             TRUE, itsMIDITime, midiGetEverything, INBUFSIZE, LeftyReadHook);
  286.     itsMIDIIn->LoadPatches('Port', INPORT);        // Did we have any patches?
  287.  
  288.     return;
  289. }
  290.  
  291. /*
  292.  *--- UpdateMenus -----------------------------------------
  293.  */
  294. void CLeftyApp::UpdateMenus(void)
  295. {
  296.     register ProcPtr    theProc;
  297.     
  298.     inherited::UpdateMenus();
  299.     
  300.     gBartender->EnableCmd(cmdEnable);
  301.     gBartender->EnableCmd(cmdDisable);
  302.     
  303.     theProc = itsMIDIIn->GetReadHook();
  304.     
  305.     gBartender->CheckMarkCmd(cmdEnable, (theProc == LeftyReadHook));
  306.     gBartender->CheckMarkCmd(cmdDisable, (theProc == NormalReadHook));
  307. }
  308.  
  309. /*
  310.  *--- DoCommand -------------------------------------------
  311.  */
  312. void CLeftyApp::DoCommand(long theCommand)
  313. {
  314.     switch (theCommand)
  315.     {
  316.         case cmdEnable:
  317.             itsMIDIIn->SetReadHook(LeftyReadHook);
  318.             break;
  319.         case cmdDisable:
  320.             itsMIDIIn->SetReadHook(NormalReadHook);
  321.             break;
  322.         case cmdAbout:
  323.             PositionDialog('ALRT', ABOUTBOX);    
  324.             Alert(ABOUTBOX, FALSE);
  325.             break;
  326.         default:
  327.             inherited::DoCommand(theCommand);
  328.             break;
  329.     }
  330. }
  331.  
  332. /*
  333.  *--- Exit -------------------------------------------------------
  334.  * Log out of the MIDI Manager, and quit.
  335.  *----------------------------------------------------------------
  336.  */
  337. void CLeftyApp::Exit()
  338. {    
  339.     itsMIDIIn->SavePatches('Port', INPORT);
  340.     itsMIDIOut->SavePatches('Port', OUTPORT);
  341.     itsMIDITime->SavePatches('Port', TIMEPORT);
  342.  
  343.     itsMIDIIn->Dispose();
  344.     itsMIDIOut->Dispose();
  345.     itsMIDITime->Dispose();
  346.  
  347.     gMIDIClient->Dispose();    // Sign out of MIDI Manager
  348.      
  349.     ExitToShell();
  350. }
  351.  
  352.  
  353. /* end of Lefty.c */
  354.